package com.spzx.channel.service.impl;

import com.spzx.channel.service.TestService;
import com.spzx.common.core.utils.StringUtils;
import com.spzx.common.core.utils.uuid.IdUtils;
import com.spzx.common.redis.util.DistributedLockUtil;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class TestServiceImpl implements TestService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    //添加synchronized锁，解决线程安全问题，但是效率低
    /* @Override
    public synchronized void testLock() {
        String test = stringRedisTemplate.opsForValue().get("test");
        if (StringUtils.isEmpty(test)) {
            stringRedisTemplate.opsForValue().set("test", "1", 2, TimeUnit.HOURS);
            return;
        }
        int num = Integer.parseInt(test);
        stringRedisTemplate.opsForValue().set("test", String.valueOf(++num));
    }*/
    //V1版 使用redis的setnx命令实现分布式锁，但是会出现死锁问题
    /*    @Override
        public synchronized void testLock() {
            //判断获取锁的状态
            Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("spzx-test-lock", "1");
            if (flag) {
                try {
                    String test = stringRedisTemplate.opsForValue().get("test");
                    if (StringUtils.isEmpty(test)) {
                        stringRedisTemplate.opsForValue().set("test", "1", 2, TimeUnit.HOURS);
                        return;
                    }
                    int num = Integer.parseInt(test);
                    stringRedisTemplate.opsForValue().set("test", String.valueOf(++num));
                } finally {
                    stringRedisTemplate.delete("spzx-test-lock");
                }
            } else {
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                    testLock();//递归调用
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }*/
    //V2版 使用redis的setnx命令实现分布式锁，并设置过期时间，解决死锁，但是会出现误删问题
    /*   @Override
    public synchronized void testLock() {
        //判断获取锁的状态
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("spzx-test-lock", "1", 10, TimeUnit.SECONDS);
        if (flag) {
            try {
                String test = stringRedisTemplate.opsForValue().get("test");
                if (StringUtils.isEmpty(test)) {
                    stringRedisTemplate.opsForValue().set("test", "1", 2, TimeUnit.HOURS);
                    return;
                }
                int num = Integer.parseInt(test);
                stringRedisTemplate.opsForValue().set("test", String.valueOf(++num));
            } finally {
                stringRedisTemplate.delete("spzx-test-lock");
            }
        } else {
            try {
                TimeUnit.MILLISECONDS.sleep(100);
                testLock();//递归调用
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }*/
    //V3版 使用redis的setnx命令实现分布式锁，设置过期时间，解决死锁,设置唯一标识，解决误删,java代码实现,没有原子性
    /*    @Override
    public synchronized void testLock() {
        String token = IdUtils.fastSimpleUUID();
        //判断获取锁的状态
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("spzx-test-lock", token, 10, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(flag)) {
            try {
                String test = stringRedisTemplate.opsForValue().get("test");
                if (StringUtils.isEmpty(test)) {
                    stringRedisTemplate.opsForValue().set("test", "1", 2, TimeUnit.HOURS);
                    return;
                }
                int num = Integer.parseInt(test);
                stringRedisTemplate.opsForValue().set("test", String.valueOf(++num));
            } finally {
                String redisToken = stringRedisTemplate.opsForValue().get("spzx-test-lock");
                if (!StringUtils.isEmpty(redisToken) && token.equals(redisToken)) {
                    stringRedisTemplate.delete("spzx-test-lock");
                }
            }
        } else {
            try {
                TimeUnit.MILLISECONDS.sleep(100);
                testLock();//递归调用
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }*/
    //V4版 使用redis的setnx命令实现分布式锁，设置过期时间，解决死锁,设置唯一标识，解决误删,使用LUA脚本保证原子性
     /*@Override
    public synchronized void testLock() {
        String lockPrefix = "spzx-test-lock";
        String token = IdUtils.fastSimpleUUID();
        int lockTimeout = 10;
        //判断获取锁的状态
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(lockPrefix, token, lockTimeout, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(flag)) {
            try {
                String test = stringRedisTemplate.opsForValue().get("test");
                if (StringUtils.isEmpty(test)) {
                    stringRedisTemplate.opsForValue().set("test", "1", 2, TimeUnit.HOURS);
                    return;
                }
                int num = Integer.parseInt(test);
                stringRedisTemplate.opsForValue().set("test", String.valueOf(++num));
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                String delLuaScript = """
                        if redis.call('get', KEYS[1]) == ARGV[1]
                        then return redis.call('del', KEYS[1])
                        else return 0 end
                        """;
                Boolean delFlag = stringRedisTemplate.execute(new DefaultRedisScript<>(delLuaScript, Boolean.class), List.of(lockPrefix), token);
                System.out.println(Boolean.TRUE.equals(delFlag) ? "删除成功" : "删除失败");
            }
        } else {
            try {
                TimeUnit.MILLISECONDS.sleep(100);
                testLock();//递归调用
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }*/
    //V5版 使用redis的setnx命令实现分布式锁，设置过期时间，解决死锁,设置唯一标识，解决误删,设置看门狗，自动续期，解决锁过期，业务逻辑没执行完的问题
    @Override
    public synchronized void testLock() {
        String lockPrefix = "spzx-test-lock";
        String token = IdUtils.fastSimpleUUID();
        int lockTimeout = 10;
        DistributedLockUtil.builder(stringRedisTemplate).getLock(lockPrefix,token,lockTimeout);
        try {
            String test = stringRedisTemplate.opsForValue().get("test");
            if (StringUtils.isEmpty(test)) {
                stringRedisTemplate.opsForValue().set("test", "1", 2, TimeUnit.HOURS);
                return;
            }
            TimeUnit.SECONDS.sleep(20);
            int num = Integer.parseInt(test);
            stringRedisTemplate.opsForValue().set("test", String.valueOf(++num));
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            DistributedLockUtil.builder(stringRedisTemplate).unLock(lockPrefix, token); //释放锁
        }
    }
}


